const redis = require('redis');
require('dotenv').config();

class RedisClient {
  constructor() {
    this.client = null;
    this.isConnected = false;
    this.cache = new Map(); // 内存缓存回退
    this.init();
  }

  async init() {
    try {
      // Redis连接配置
      const redisConfig = {
        socket: {
          host: process.env.REDIS_HOST || 'localhost',
          port: process.env.REDIS_PORT || 6379,
          reconnectStrategy: (retries) => {
            if (retries > 10) {
              console.log('Too many retries on Redis. Connection terminated');
              return new Error('Too many retries');
            }
            return Math.min(retries * 100, 3000);
          }
        },
        password: process.env.REDIS_PASSWORD || undefined,
        database: process.env.REDIS_DB || 0
      };

      this.client = redis.createClient(redisConfig);

      this.client.on('connect', () => {
        console.log('Redis client connecting...');
      });

      this.client.on('ready', () => {
        this.isConnected = true;
        console.log('Redis client connected successfully');
      });

      this.client.on('error', (err) => {
        console.error('Redis Client Error:', err.message);
        this.isConnected = false;
      });

      this.client.on('end', () => {
        console.log('Redis client disconnected');
        this.isConnected = false;
      });

      this.client.on('reconnecting', () => {
        console.log('Redis client reconnecting...');
      });

      await this.client.connect();
    } catch (error) {
      console.error('Failed to initialize Redis:', error.message);
      console.log('Falling back to memory cache');
    }
  }

  async set(key, value, expire = 300) {
    try {
      if (this.isConnected && this.client) {
        const serializedValue = JSON.stringify(value);
        if (expire) {
          await this.client.setEx(key, expire, serializedValue);
        } else {
          await this.client.set(key, serializedValue);
        }
        return true;
      } else {
        // 内存缓存回退
        const cacheItem = {
          data: value,
          expires: expire ? Date.now() + expire * 1000 : null
        };
        this.cache.set(key, cacheItem);
        return true;
      }
    } catch (error) {
      console.error('Redis set error:', error.message);
      return false;
    }
  }

  async get(key) {
    try {
      if (this.isConnected && this.client) {
        const data = await this.client.get(key);
        return data ? JSON.parse(data) : null;
      } else {
        // 内存缓存回退
        const item = this.cache.get(key);
        if (!item) return null;
        
        if (item.expires && Date.now() > item.expires) {
          this.cache.delete(key);
          return null;
        }
        
        return item.data;
      }
    } catch (error) {
      console.error('Redis get error:', error.message);
      return null;
    }
  }

  async del(key) {
    try {
      if (this.isConnected && this.client) {
        await this.client.del(key);
        return true;
      } else {
        this.cache.delete(key);
        return true;
      }
    } catch (error) {
      console.error('Redis delete error:', error.message);
      return false;
    }
  }

  async exists(key) {
    try {
      if (this.isConnected && this.client) {
        return await this.client.exists(key);
      } else {
        const item = this.cache.get(key);
        if (!item) return false;
        
        if (item.expires && Date.now() > item.expires) {
          this.cache.delete(key);
          return false;
        }
        
        return true;
      }
    } catch (error) {
      console.error('Redis exists error:', error.message);
      return false;
    }
  }

  async getOrSet(key, fetchData, expire = 300) {
    try {
      // 尝试从缓存获取
      const cachedData = await this.get(key);
      if (cachedData) {
        console.log('Cache hit for:', key);
        return cachedData;
      }

      console.log('Cache miss for:', key);
      // 缓存中没有，获取新数据
      const freshData = await fetchData();
      if (freshData) {
        await this.set(key, freshData, expire);
      }
      return freshData;
    } catch (error) {
      console.error('Cache getOrSet error:', error.message);
      return await fetchData();
    }
  }

  async disconnect() {
    if (this.isConnected && this.client) {
      await this.client.quit();
    }
  }
}

module.exports = new RedisClient();